#include <websocketpp/config/core.hpp>

#include <websocketpp/server.hpp>

#include <iostream>
#include <fstream>

typedef websocketpp::server<websocketpp::config::core> server;

using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
using websocketpp::lib::bind;

// pull out the type of messages sent by our config
typedef server::message_ptr message_ptr;

// Define a callback to handle incoming messages
void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg) {
	if (msg->get_opcode() == websocketpp::frame::opcode::text) {
		s->get_alog().write(websocketpp::log::alevel::app,
			"Text Message Received: " + msg->get_payload());
	}
	else {
		s->get_alog().write(websocketpp::log::alevel::app,
			"Binary Message Received: " + websocketpp::utility::to_hex(msg->get_payload()));
	}

	try {
		s->send(hdl, msg->get_payload(), msg->get_opcode());
	}
	catch (websocketpp::exception const & e) {
		s->get_alog().write(websocketpp::log::alevel::app,
			std::string("Echo Failed: ") + e.what());
	}
}

int main() {
	server s;
	std::ofstream log;

	try {
		// set up access channels to only log interesting things
		s.clear_access_channels(websocketpp::log::alevel::all);
		s.set_access_channels(websocketpp::log::alevel::connect);
		s.set_access_channels(websocketpp::log::alevel::disconnect);
		s.set_access_channels(websocketpp::log::alevel::app);

		// Log to a file rather than stdout, as we are using stdout for real
		// output
		log.open("output.log");
		s.get_alog().set_ostream(&log);
		s.get_elog().set_ostream(&log);

		// print all output to stdout
		s.register_ostream(&std::cout);

		// Register our message handler
		s.set_message_handler(bind(&on_message, &s, ::_1, ::_2));

		server::connection_ptr con = s.get_connection();

		con->start();

		// C++ iostream's don't support the idea of asynchronous i/o. As such
		// there are two input strategies demonstrated here. Buffered I/O will
		// read from stdin in chunks until EOF. This works very well for
		// replaying canned connections as would be done in automated testing.
		//
		// If the server is being used live however, assuming input is being
		// piped from elsewhere in realtime, this strategy will result in small
		// messages being buffered forever. The non-buffered strategy below
		// reads characters from stdin one at a time. This is inefficient and
		// for more serious uses should be replaced with a platform specific
		// asyncronous i/o technique like select, poll, IOCP, etc
		bool buffered_io = false;

		if (buffered_io) {
			std::cin >> *con;
			con->eof();
		}
		else {
			char a;
			while (std::cin.get(a)) {
				con->read_some(&a, 1);
			}
			con->eof();
		}
	}
	catch (websocketpp::exception const & e) {
		std::cout << e.what() << std::endl;
	}
	log.close();
}
